// SKNotes - a note taking and gathering tool
// Copyright (C) 2009  Stefan Kueng
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace SKNotes.Configuration
{
    using System;
    using System.Windows;
    using System.ComponentModel;
    using System.Diagnostics;

	public class FrameworkElementSettings : ApplicationSettings
	{
		public static readonly DependencyProperty SettingsProperty =
			DependencyProperty.RegisterAttached(
				"Settings",
				typeof( string ),
				typeof( FrameworkElementSettings ),
				new FrameworkPropertyMetadata( new PropertyChangedCallback( OnFrameworkElementSettingsChanged ) ) );

		public static readonly DependencyProperty CollectedSettingProperty =
			DependencyProperty.RegisterAttached(
				"CollectedSetting",
				typeof( DependencyProperty ),
				typeof( FrameworkElementSettings ),
				new FrameworkPropertyMetadata( new PropertyChangedCallback( OnCollectedSettingChanged ) ) );

		public static readonly DependencyProperty ExcludeElementProperty = DependencyProperty.RegisterAttached(
			"ExcludeElement",
			typeof( bool ),
			typeof( FrameworkElementSettings ) );

		public FrameworkElementSettings( FrameworkElement frameworkElement ) :
			this( frameworkElement, frameworkElement.GetType().Name )
		{
		}

		public FrameworkElementSettings( FrameworkElement frameworkElement, string settingsKey ) :
			base( settingsKey )
		{
			if ( frameworkElement == null )
			{
				throw new ArgumentNullException( "frameworkElement" );
			}

			this.frameworkElement = frameworkElement;
			this.frameworkElement.Initialized += new EventHandler( FrameworkElementInitialized );
		}

		public FrameworkElement FrameworkElement
		{
			get { return this.frameworkElement; }
		}

		public bool SaveOnClose
		{
			get { return this.saveOnClose; }
			set { this.saveOnClose = value; }
		}

		private Window ParentWindow
		{
			get
			{
				DependencyObject control = this.frameworkElement;
				while ( control != null )
				{
					if ( control is Window )
					{
						return control as Window;
					}

					control = LogicalTreeHelper.GetParent( control );
				}

				return null;
			}
		} 

		public static string GetSettings( DependencyObject dependencyObject )
		{
			return dependencyObject.GetValue( SettingsProperty ) as string;
		} 

		public static void SetSettings( DependencyObject dependencyObject, string settingsKey )
		{
			dependencyObject.SetValue( SettingsProperty, settingsKey );
		} 

		private void FrameworkElementInitialized( object sender, EventArgs e )
		{
			Window window = ParentWindow;
			if ( window == null )
			{
				throw new InvalidOperationException();
			}

			// subscribe to the parent window events
			window.Loaded += new RoutedEventHandler( WindowLoaded );
			window.Closing += new CancelEventHandler( WindowClosing );
		}

		private void WindowLoaded( object sender, RoutedEventArgs e )
		{
			Load();
		}

		private void WindowClosing( object sender, CancelEventArgs e )
		{
			if ( this.saveOnClose == false )
			{
				return;
			}
			Save();
		} 

		private static void OnFrameworkElementSettingsChanged( DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e )
		{
			FrameworkElement frameworkElement = dependencyObject as FrameworkElement;
			if ( frameworkElement == null )
			{
				Debug.WriteLine( "FrameworkElementSettings: invalid framework element" );
				return;
			}

			if ( frameworkElement.GetValue( DependencyPropertySetting.ApplicationSettingsProperty ) != null )
			{
				return; // framework-element contains already an application setting
			}

			string settingsKey = e.NewValue as string;
			if ( string.IsNullOrEmpty( settingsKey ) )
			{
				Debug.WriteLine( "FrameworkElementSettings: missing framework element settings key" );
				return;
			}

			// create and attach the application settings to the framework-element
			FrameworkElementSettings frameworkElementSettings = new FrameworkElementSettings( frameworkElement, settingsKey );
			frameworkElement.SetValue( DependencyPropertySetting.ApplicationSettingsProperty, frameworkElementSettings );
		} 

		private static void OnCollectedSettingChanged( DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e )
		{
			FrameworkElement frameworkElement = dependencyObject as FrameworkElement;
			if ( frameworkElement == null )
			{
				Debug.WriteLine( "FrameworkElementSettings: invalid framework element" );
				return;
			}

			DependencyProperty dependencyProperty = e.NewValue as DependencyProperty;
			if ( dependencyProperty == null )
			{
				Debug.WriteLine( "FrameworkElementSettings: missing dependency property" );
				return;
			}

			// search the framework element settings
			FrameworkElementSettings frameworkElementSettings =
					frameworkElement.ReadLocalValue( DependencyPropertySetting.ApplicationSettingsProperty ) as FrameworkElementSettings;
			if ( frameworkElementSettings == null )
			{
				Debug.WriteLine( "FrameworkElementSettings: missing framework element settings in element " + frameworkElement );
				return;
			}

			DependencyPropertySettingCollector collector =
				new DependencyPropertySettingCollector( frameworkElement, dependencyProperty );
			frameworkElementSettings.SettingCollectors.Add( collector );
		} 

		private readonly FrameworkElement frameworkElement;
		private bool saveOnClose = true;
	}
} 
